package org.finesys.codegen.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.NamingCase;
import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.finesys.codegen.entity.*;
import org.finesys.codegen.enums.BooleanFilledEnum;
import org.finesys.codegen.properties.CodeGenDefaultProperties;
import org.finesys.codegen.service.*;
import org.finesys.codegen.util.VelocityKit;
import org.finesys.common.core.util.FileUtil;
import org.finesys.common.core.util.StrUtil;
import org.springframework.boot.SpringBootVersion;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
@RequiredArgsConstructor
public class GeneratorServiceImpl implements GeneratorService {

    private final GenDatasourceConfService genDatasourceConfService;
    private final GenTableService genTableService;
    private final GenTableColumnService genTableColumnService;
    private final GenFieldTypeService genFieldTypeService;
    private final GenTemplateGroupService genTemplateGroupService;
    private final GenTableGroupService genTableGroupService;
    private final GenTemplateService genTemplateService;
    private final CodeGenDefaultProperties codeGenDefaultProperties;
    private final GenGroupService genGroupService;

    @Override
    @SneakyThrows
    public void download(Long tableId, ZipOutputStream zipOutputStream) {
        //数据模型
        Map<String, Object> dataModel = getDataModel(tableId);
        //获取表格分组信息
        List<GenTableGroup> tableGroupList = genTableGroupService.list(Wrappers.<GenTableGroup>lambdaQuery().eq(GenTableGroup::getTableId, tableId));
        //获取表对应的分组
        for (GenTableGroup genTableGroup : tableGroupList) {
            //分组信息
            Long groupId = genTableGroup.getGroupId();
            //分组
            GenGroup genGroup = genGroupService.getById(groupId);
            //分组名称
            String groupName = genGroup.getGroupName();
            //创建文件夹
            ZipEntry zipEntry = new ZipEntry(groupName + File.pathSeparator);
            zipOutputStream.putNextEntry(zipEntry);
            zipOutputStream.closeEntry();
            //获取模版ID数据
            List<GenTemplateGroup> genTemplateGroupList = genTemplateGroupService.
                    list(Wrappers.<GenTemplateGroup>lambdaQuery().eq(GenTemplateGroup::getGroupId, groupId));
            //获取模版ID
            List<Long> templateIds = genTemplateGroupList.stream().map(genTemplateGroup -> genTemplateGroup.getTemplateId()).collect(Collectors.toList());
            //获取模版数据
            List<GenTemplate> genTemplateList = genTemplateService.listByIds(templateIds);
            //解下模板数据
            for (GenTemplate genTemplate : genTemplateList) {
                String templateCode = genTemplate.getTemplateCode();
                String templatePath = genTemplate.getGeneratorPath();
                String content = VelocityKit.renderStr(templateCode, dataModel);
                String path = VelocityKit.renderStr(templatePath, dataModel);
                //添加样式目录
                path = groupName + File.pathSeparator + path;
                //添加到Zip
                zipOutputStream.putNextEntry(new ZipEntry(path));
                IoUtil.writeUtf8(zipOutputStream, false, content);
                zipOutputStream.flush();
                zipOutputStream.closeEntry();
            }
        }
    }

    /**
     * 通过 Lambda 表达式优化的获取数据模型方法
     *
     * @param tableId 表格 ID
     * @return 数据模型 Map 对象
     */
    private Map<String, Object> getDataModel(Long tableId) {
        // 获取表格信息
        GenTable genTable = genTableService.getById(tableId);
        //数据源
        GenDatasourceConf genDatasourceConf = genDatasourceConfService.getOne(Wrappers.<GenDatasourceConf>lambdaQuery().eq(GenDatasourceConf::getName, genTable.getDsName()));
        String dbType = "";
        if (genDatasourceConf != null) {
            dbType = genDatasourceConf.getDsType();
        } else {
            //默认数据库类型为 MySQL
            dbType = "mysql";
        }
        //获取子表信息
        getSubTable(genTable);
        //创建数据模型
        Map<String, Object> dataModel = new HashMap<>();
        // 填充数据模型
        dataModel.put("opensource", true);
        dataModel.put("isSpringBoot3", isSpringBoot3());
        dataModel.put("dbType", dbType);
        dataModel.put("package", genTable.getPackageName());
        dataModel.put("packagePath", genTable.getPackageName().replace(".", "/"));
        dataModel.put("version", genTable.getVersion());
        dataModel.put("moduleName", genTable.getModuleName());
        dataModel.put("ModuleName", StrUtil.upperFirst(genTable.getModuleName()));
        dataModel.put("functionName", genTable.getFunctionName());
        dataModel.put("FunctionName", StrUtil.upperFirst(genTable.getFunctionName()));
        dataModel.put("formLayout", genTable.getFormCols());
        dataModel.put("author", genTable.getFunctionAuthor());
        dataModel.put("datetime", DateUtil.now());
        dataModel.put("date", DateUtil.today());
        dataModel.put("pk", genTable.getPrimaryKeyColumn());
        //将表字段按照类型分组并存储到数据模型中
        setFieldTypeList(dataModel, genTable);
        // 获取导入的包列表
        Set<String> importList = genFieldTypeService.getPackageByTableId(tableId);
        dataModel.put("importList", importList);
        dataModel.put("tableName", genTable.getTableName());
        dataModel.put("tableComment", genTable.getTableComment());
        dataModel.put("className", StrUtil.lowerFirst(genTable.getClassName()));
        dataModel.put("ClassName", genTable.getClassName());
        dataModel.put("columnList", genTable.getColumnList());
        if (StrUtil.isNotEmpty(genTable.getBackendPath())) {
            dataModel.put("backendPath", genTable.getBackendPath());
        } else {
            dataModel.put("backendPath", codeGenDefaultProperties.getBackendPath());
        }
        if (StrUtil.isNotEmpty(genTable.getFrontendPath())) {
            dataModel.put("frontendPath", genTable.getFrontendPath());
        } else {
            dataModel.put("frontendPath", codeGenDefaultProperties.getFrontendPath());
        }
        String subTableName = genTable.getSubTableName();
        if (StrUtil.isNotEmpty(subTableName)) {
            dataModel.put("subTable", genTable.getSubTable());
            dataModel.put("subTableName", subTableName);
            dataModel.put("subTableFkName", genTable.getSubTableFkName());
            dataModel.put("subColumnList", genTable.getSubTable().getColumnList());
            dataModel.put("SubClassName", NamingCase.toPascalCase(subTableName));
            dataModel.put("subClassName", StrUtil.lowerFirst(NamingCase.toPascalCase(subTableName)));
            genTable.getSubTable().getColumnList().stream().filter(column -> column.getColumnName().equals("tenant_id")).findFirst().ifPresent(column -> {
                dataModel.put("isSubTenant", true);
            });
        }
        // 设置是否是多租户模式 (判断字段列表中是否包含 tenant_id 字段)
        genTable.getColumnList()
                .stream()
                .filter(genTableColumnEntity -> genTableColumnEntity.getColumnName().equals("tenant_id"))
                .findFirst()
                .ifPresent(columnEntity -> dataModel.put("isTenant", true));
        return dataModel;
    }

    /**
     * 将表字段按照类型分组并存储到数据模型中
     *
     * @param dataModel 存储数据的 Map 对象
     * @param genTable  表信息对象
     */
    private void setFieldTypeList(Map<String, Object> dataModel, GenTable genTable) {
        // 按字段类型分组，使用 Map 存储不同类型的字段列表
        Map<Boolean, List<GenTableColumn>> typeMap = genTable.getColumnList()
                .stream()
                .collect(Collectors.partitioningBy(columnEntity -> BooleanUtil.toBoolean(columnEntity.getPrimaryPk())));

        // 从分组后的 Map 中获取不同类型的字段列表
        List<GenTableColumn> primaryList = typeMap.get(true);
        List<GenTableColumn> formList = typeMap.get(false)
                .stream()
                .filter(columnEntity -> BooleanUtil.toBoolean(columnEntity.getFormItem()))
                .collect(Collectors.toList());
        List<GenTableColumn> gridList = typeMap.get(false)
                .stream()
                .filter(columnEntity -> BooleanUtil.toBoolean(columnEntity.getGridItem()))
                .collect(Collectors.toList());
        List<GenTableColumn> queryList = typeMap.get(false)
                .stream()
                .filter(columnEntity -> BooleanUtil.toBoolean(columnEntity.getQueryItem()))
                .collect(Collectors.toList());
        dataModel.put("primaryList", primaryList);
        dataModel.put("formList", formList);
        dataModel.put("gridList", gridList);
        dataModel.put("queryList", queryList);
    }

    /**
     * 判断当前是否是 SpringBoot3 版本
     *
     * @return true/fasle
     */
    private Object isSpringBoot3() {
        return StrUtil.startWith(SpringBootVersion.getVersion(), "3");
    }

    /**
     * 获取子表信息
     */
    private void getSubTable(GenTable genTable) {
        //表字段信息
        List<GenTableColumn> genTableColumnList = genTableColumnService.list(Wrappers.<GenTableColumn>lambdaQuery().eq(GenTableColumn::getTableId, genTable.getTableId()).orderByAsc(GenTableColumn::getSort));
        //表字段信息
        genTable.setColumnList(genTableColumnList);
        //主键信息
        GenTableColumn primaryKeyColumn = genTableColumnList.stream().filter(genTableColumn -> BooleanFilledEnum.TRUE.getValue().equals(genTableColumn.getPrimaryPk())).findFirst().orElseGet(null);
        if (primaryKeyColumn != null) {
            genTable.setPrimaryKeyColumn(primaryKeyColumn);
        }
        //子表信息
        if (StrUtil.isNotEmpty(genTable.getSubTableName())) {
            // 获取表格信息
            GenTable subGenTable = genTableService.getOne(Wrappers.<GenTable>lambdaQuery().eq(GenTable::getTableName, genTable.getSubTableName()));
            getSubTable(subGenTable);
        }
    }

    /**
     * 预览代码
     */
    @Override
    public List<Map<String, String>> preview(Long tableId) {

        //数据模型
        Map<String, Object> dataModel = getDataModel(tableId);
        //获取表格分组信息
        List<GenTableGroup> tableGroupList = genTableGroupService.list(Wrappers.<GenTableGroup>lambdaQuery().eq(GenTableGroup::getTableId, tableId));

        List<Map<String, String>> resultList = new ArrayList<>();
        //获取表对应的分组
        tableGroupList.stream().forEach(genTableGroup -> {

            //分组信息
            Long groupId = genTableGroup.getGroupId();
            //获取模版ID数据
            List<GenTemplateGroup> genTemplateGroupList = genTemplateGroupService.
                    list(Wrappers.<GenTemplateGroup>lambdaQuery().eq(GenTemplateGroup::getGroupId, groupId));
            //获取模版ID
            List<Long> templateIds = genTemplateGroupList.stream().map(genTemplateGroup -> genTemplateGroup.getTemplateId()).collect(Collectors.toList());
            //获取模版数据
            List<GenTemplate> genTemplateList = genTemplateService.listByIds(templateIds);
            //解下模板数据
            for (GenTemplate genTemplate : genTemplateList) {
                String templateCode = genTemplate.getTemplateCode();
                String templatePath = genTemplate.getGeneratorPath();
                String content = VelocityKit.renderStr(templateCode, dataModel);
                String path = VelocityKit.renderStr(templatePath, dataModel);
                // 使用 map 简化代码
                Map<String, String> map = new HashMap<String, String>(4) {
                    {
                        put("code", content);
                        put("codePath", path);
                    }
                };
                resultList.add(map);
            }
        });
        return resultList;
    }

    /**
     * 目标目录写入渲染结果方法
     *
     * @param tableId 表
     */
    @Override
    public void generatorCode(Long tableId) {
        //数据模型
        Map<String, Object> dataModel = getDataModel(tableId);
        //获取表格分组信息
        List<GenTableGroup> tableGroupList = genTableGroupService.list(Wrappers.<GenTableGroup>lambdaQuery().eq(GenTableGroup::getTableId, tableId));
        //获取表对应的分组
        for (GenTableGroup genTableGroup : tableGroupList) {
            //分组信息
            Long groupId = genTableGroup.getGroupId();
            //获取模版ID数据
            List<GenTemplateGroup> genTemplateGroupList = genTemplateGroupService.
                    list(Wrappers.<GenTemplateGroup>lambdaQuery().eq(GenTemplateGroup::getGroupId, groupId));
            //获取模版ID
            List<Long> templateIds = genTemplateGroupList.stream().map(genTemplateGroup -> genTemplateGroup.getTemplateId()).collect(Collectors.toList());
            //获取模版数据
            List<GenTemplate> genTemplateList = genTemplateService.listByIds(templateIds);
            //解下模板数据
            for (GenTemplate genTemplate : genTemplateList) {
                String templateCode = genTemplate.getTemplateCode();
                String templatePath = genTemplate.getGeneratorPath();
                String content = VelocityKit.renderStr(templateCode, dataModel);
                String path = VelocityKit.renderStr(templatePath, dataModel);
                FileUtil.writeUtf8String(content, path);
            }
        }
    }
}
